package com.flyqiu.flow.starter;

import com.flyqiu.flow.api.FlyQiuFlowConfig;
import com.flyqiu.flow.api.FlyQiuFlowEngine;
import com.flyqiu.flow.api.cache.FlyQiuFlowCache;
import com.flyqiu.flow.api.cache.FlyQiuFlowCacheManage;
import com.flyqiu.flow.api.candidate.FlowCandidateLoad;
import com.flyqiu.flow.api.candidate.FlyQiyFlowCandidateManage;
import com.flyqiu.flow.api.context.FlyQiuFlowGlobalContext;
import com.flyqiu.flow.api.deploy.FlyQiuFlowDeployManage;
import com.flyqiu.flow.api.deploy.IFlowDeploy;
import com.flyqiu.flow.api.event.FlowEngineEvent;
import com.flyqiu.flow.api.format.FlyQiuFlowBpmnFormatManage;
import com.flyqiu.flow.api.format.IBpmnFormat;
import com.flyqiu.flow.api.script.FlyQiuFlowScriptActuator;
import com.flyqiu.flow.api.script.FlyQiuFlowScriptActuatorManage;
import com.flyqiu.flow.core.BaseFlowEngineImpl;
import com.flyqiu.flow.event.ActInstEvent;
import com.flyqiu.flow.event.ProcInstEvent;
import com.flyqiu.flow.event.UserTaskEvent;
import com.flyqiu.flow.event.api.FlyQiuFlowEventManage;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;


/**
 * spring flow engine
 */
@Slf4j
@Component
@AutoConfigureAfter(value = {FlyQiuFlowManageAutoConfig.class, DataSourceAutoConfiguration.class})
@ConditionalOnMissingBean(FlyQiuFlowEngine.class)
public class FlyQiuSpringFlowEngine extends BaseFlowEngineImpl implements InitializingBean {


    private final ApplicationContext applicationContext;
    private final FlyQiuFlowConfig procConfig;

    public FlyQiuSpringFlowEngine(FlyQiuFlowSpringGlobalContext flowSpringGlobalContext,
                                  FlyQiuFlowConfig config,
                                  ApplicationContext applicationContext) {
        globalContext = flowSpringGlobalContext;
        this.procConfig = config;
        this.applicationContext = applicationContext;
    }

    @Override
    public void afterPropertiesSet() {
        initEngine(procConfig);
    }

    @Override
    protected void registerFlowEvents(FlyQiuFlowEventManage eventManage, FlyQiuFlowConfig procConfig) {
        super.registerFlowEvents(eventManage, procConfig);
        //加载引擎事件
        ObjectProvider<FlowEngineEvent> flowEngineEvents = applicationContext.getBeanProvider(FlowEngineEvent.class);
        for (FlowEngineEvent event : flowEngineEvents) {
            if (event instanceof FlyQiuFlowEventManage) {
                continue;
            }
            eventManage.addEvent(FlowEngineEvent.class, event);
        }

        //加载活动事件
        ObjectProvider<ActInstEvent> activityEvents = applicationContext.getBeanProvider(ActInstEvent.class);
        for (ActInstEvent event : activityEvents) {
            if (event instanceof FlyQiuFlowEventManage) {
                continue;
            }
            eventManage.addEvent(ActInstEvent.class, event);
        }

        //加载流程实例事件
        ObjectProvider<ProcInstEvent> procInstEvents = applicationContext.getBeanProvider(ProcInstEvent.class);
        for (ProcInstEvent event : procInstEvents) {
            if (event instanceof FlyQiuFlowEventManage) {
                continue;
            }

            eventManage.addEvent(ProcInstEvent.class, event);
        }

        //加载任务事件
        ObjectProvider<UserTaskEvent> taskEvents = applicationContext.getBeanProvider(UserTaskEvent.class);
        for (UserTaskEvent event : taskEvents) {
            if (event instanceof FlyQiuFlowEventManage) {
                continue;
            }
            eventManage.addEvent(UserTaskEvent.class, event);
        }


    }


    @Override
    protected void initCoreService() {

    }

    @Override
    protected void registerFlowDeploy(FlyQiuFlowDeployManage manage, FlyQiuFlowGlobalContext context) {
        super.registerFlowDeploy(manage, context);

        //加载流程部署器
        ObjectProvider<IFlowDeploy> deploys = applicationContext.getBeanProvider(IFlowDeploy.class);
        for (IFlowDeploy cache : deploys) {
            manage.registerProcDeploy(cache);
        }
    }

    @Override
    protected void registerFlowCaches(FlyQiuFlowCacheManage flyQiuFlowCacheManage, FlyQiuFlowConfig procConfig) {
        super.registerFlowCaches(flyQiuFlowCacheManage, procConfig);

        ObjectProvider<FlyQiuFlowCache> flowCaches = applicationContext.getBeanProvider(FlyQiuFlowCache.class);
        for (FlyQiuFlowCache cache : flowCaches) {
            flyQiuFlowCacheManage.registerCache(cache);
        }
    }

    @Override
    protected void registerFlowScriptActuators(FlyQiuFlowScriptActuatorManage flyQiuFlowScriptActuatorManage, FlyQiuFlowConfig procConfig) {
        super.registerFlowScriptActuators(flyQiuFlowScriptActuatorManage, procConfig);

        ObjectProvider<FlyQiuFlowScriptActuator> actuators = applicationContext.getBeanProvider(FlyQiuFlowScriptActuator.class);
        for (FlyQiuFlowScriptActuator actuator : actuators) {
            flyQiuFlowScriptActuatorManage.register(actuator);
        }
    }

    @Override
    protected void registerBpmnFormat(FlyQiuFlowBpmnFormatManage flyQiuFlowBpmnFormatManage, FlyQiuFlowConfig procConfig) {
        super.registerBpmnFormat(flyQiuFlowBpmnFormatManage, procConfig);
        ObjectProvider<IBpmnFormat> formats = applicationContext.getBeanProvider(IBpmnFormat.class);
        for (IBpmnFormat format : formats) {
            flyQiuFlowBpmnFormatManage.registerBpmnFormat(format);
        }
    }

    @Override
    protected void registerCandidate(FlyQiyFlowCandidateManage flowCandidateManage, FlyQiuFlowConfig procConfig) {
        super.registerCandidate(flowCandidateManage, procConfig);
        //加载候选项加载器

        ObjectProvider<FlowCandidateLoad> candidateLoads = applicationContext.getBeanProvider(FlowCandidateLoad.class);
        for (FlowCandidateLoad load : candidateLoads) {
            flowCandidateManage.register(load);
        }
    }


}
